package com.park.server.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JacksonJsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.Assert;

import com.park.server.bean.RedisFiled;


/**
 * @description [ redis的公共类 ]
 * 
 * @author caizh
 * @date 2014-12-22 上午9:58:04
 * @version v1.0
 */
public abstract class AbstractRedis<T, E> {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    protected RedisTemplate<String, Object> stringRedisTemplate;
    protected Logger logger = LoggerFactory.getLogger(getClass());


    protected abstract BaseMapper<T, Integer, E> getMapper();


    /**
     * 存储实体的key
     * 
     * @return
     */
    public abstract String key();


    /**
     * 存储id集合的key
     * 
     * @return
     */
    public abstract String listKeys();

    private Class<T> entityClass;

    RedisSerializer<T> rs = null;


    protected List<Object> executePipeline(RedisCallback<List<Object>> pipelineCallback) {
        return (List<Object>) redisTemplate.execute(pipelineCallback);
    }


    public AbstractRedis() {
        Type c = (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        if (c instanceof ParameterizedType) {
            entityClass = (Class<T>) ((ParameterizedType) c).getRawType();
        }
        else {
            entityClass = (Class<T>) c;
        }
    }


    /**
     * @description 初始化后从数据库获取最新一条数据id并赋值给redis自增长 防止出现竞态
     * @date 2014-9-11 下午5:13:44
     * @updateAuthor 余乙迪
     * @updateDate 2014-9-11 下午5:13:44
     * @updateRemark
     * @version v1.0
     */
    @PostConstruct
    public void init() {
        rs = new JacksonJsonRedisSerializer<T>((Class<T>) entityClass);
        redisTemplate.setHashValueSerializer(rs);
    }


    /**
     * @description [ 根据子类名称获取单个hash对象 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午10:03:28
     */
    public T get(Object id) {
        Assert.notNull(id);
        return (T) redisTemplate.opsForHash().get(key(), String.valueOf(id));
    };


    /**
     * @description [ 删除自定义名称的任意对象 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午10:02:59
     * @param key
     */
    public void delKey(Object key) {
        redisTemplate.delete(String.valueOf(key));
    };


    /**
     * @description [ 根据自定义名称获取单个hash对象 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午10:03:48
     */
    public Object getStr(String key, String tag) {
        Assert.notNull(key);
        Object obj = (Object) stringRedisTemplate.opsForHash().get(key, tag);
        return obj;
    }


    /**
     * @description [ 添加自定义单个hash数据 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午9:58:31
     */
    public void saveStr(String key, String tag, Object val) {
        stringRedisTemplate.opsForHash().put(key, tag, val);
    }


    /**
     * @description [ 批量新增hash ]
     * 
     * @date 2014-10-24 下午7:28:37
     * @version v1.0
     */
    public void saveHashs(List<T> list) {
        HashOperations<String, Object, Object> hashOper = redisTemplate.opsForHash();
        Map<String, Object> map = new HashMap<String, Object>();
        for (T t : list) {
            map.put(((RedisFiled) t).filed(), t);
        }
        hashOper.putAll(key(), map);
    }


    /**
     * @description [ 添加子类名称的单个hash对象 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午9:59:04
     */
    public void saveObject(Object tag, T t) {
        redisTemplate.opsForHash().put(key(), String.valueOf(tag), t);
    }


    /**
     * @description [ 删除自定义名称的hash对象 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午10:00:07
     */
    public void delCustomHash(String key, Object field) {
        redisTemplate.opsForHash().delete(key, String.valueOf(field));
    }


    /**
     * @description [ 根据自定义名称加标示符获取list对象集合 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午10:04:58
     */
    public List<Object> getCustomAllList(String key) {
        List<Object> list = (List<Object>) stringRedisTemplate.opsForList().range(key, 0, (-1));
        return list;
    }


    /**
     * @description [ 根据自定义名称批量添加list集合对象 ]
     * 
     * @author caizh
     * @date 2014-12-22 上午10:05:17
     */
    public void saveList(String key, Object val) {
        stringRedisTemplate.opsForList().leftPush(key, String.valueOf(val));
    }


    /**
     * @description [ 根据子类名称删除list集合中单个相同的值 ]
     * 
     * @date 2014-10-24 下午7:29:11
     * @version v1.0
     */
    public void saveLists(final String key, final List<String> list) {
        stringRedisTemplate.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                for (String string : list) {
                    connection.lPush(key.getBytes(), string.getBytes());
                }
                return true;
            }
        });
    }


    /**
     * @description [ 根据子类名称删除list集合中单个相同的值 ]
     * 
     * @date 2014-10-24 下午7:29:11
     * @version v1.0
     */
    public void delList(String key, Object val) {
        stringRedisTemplate.opsForList().remove(key, 0, String.valueOf(val));
    }


    /**
     * @description [ 根据子类名称删除list集合中单个相同的值 ]
     * 
     * @date 2014-10-24 下午7:29:11
     * @version v1.0
     */
    public void delLists(final String key, final List<String> list) {
        stringRedisTemplate.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                for (String string : list) {
                    connection.lRem(key.getBytes(), 0, string.getBytes());
                }
                return true;
            }
        });
    }


    /**
     * @description 从map中根据id集合批量获取bean
     * @date 2014年10月17日 下午4:15:41
     * @updateAuthor jacarri
     * @updateDate 2014年10月17日 下午4:15:41
     * @updateRemark
     * @version v1.0
     */
    public List<T> getListFromMap(final String key, final List<Object> ids) {
        RedisCallback<List<Object>> pipelineCallback = new RedisCallback<List<Object>>() {
            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                connection.openPipeline();
                for (Object id : ids) {
                    connection.hGet(key.getBytes(), String.valueOf(id).getBytes());
                }
                return connection.closePipeline();
            }
        };
        List<Object> list = executePipeline(pipelineCallback);
        List<T> result = new ArrayList<T>();
        for (Object object : list) {
            if (null != object) {
                String obj = new String((byte[]) object);
                result.add((T) rs.deserialize(obj.getBytes()));
            }
        }
        return result;
    }
}
